package io.itit.ecp.admin.provide.action.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import icu.mhb.mybatisplus.plugln.core.JoinLambdaWrapper;
import icu.mhb.mybatisplus.plugln.extend.Joins;
import io.itit.ecp.admin.common.enums.CarbonQuotaInputTypeEnums;
import io.itit.ecp.admin.common.enums.IMeterDeviceEnums;
import io.itit.ecp.admin.provide.action.req.CarbonQuotaQueryReq;
import io.itit.ecp.admin.provide.action.resp.CarbonQuotaResp;
import io.itit.ecp.admin.provide.action.resp.CarbonQuotaStaticsResp;
import io.itit.ecp.admin.provide.helper.SceneHelper;
import io.itit.ecp.admin.server.entity.CarbonQuotaEntity;
import io.itit.ecp.admin.server.entity.EnergyDataEntity;
import io.itit.ecp.admin.server.entity.MeterDeviceEntity;
import io.itit.ecp.admin.server.entity.SceneProjectEntity;
import io.itit.ecp.admin.server.service.ICarbonQuotaService;
import io.itit.grass.common.provide.action.req.BaseBatchIdReq;
import io.itit.grass.common.provide.action.req.BaseIdReq;
import io.itit.grass.common.provide.action.resp.ResultResp;
import io.itit.grass.common.provide.action.validation.IAdd;
import io.itit.grass.common.provide.action.validation.IQueryPage;
import io.itit.grass.common.provide.action.validation.IRequired;
import io.itit.grass.common.provide.action.validation.IUpdate;
import io.itit.grass.web.wall.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 碳配额管理
 */
@Api(tags = "碳配额管理")
@RestController
@RequestMapping("/ecp-admin/carbonQuota")
public class CarbonQuotaController {

    @Resource
    private ICarbonQuotaService carbonQuotaService;

    @ApiOperation("分页查询")
    @PostMapping("queryPage")
    @RequiresPermissions("ecp-admin:carbonQuota:search")
    public ResultResp<IPage<CarbonQuotaResp>> queryPage(@RequestBody @Validated(IQueryPage.class) CarbonQuotaQueryReq req) {
        Page<CarbonQuotaResp> page = buildQW(req).joinPage(PageUtils.getPage(req, CarbonQuotaResp.class), CarbonQuotaResp.class);
        return ResultResp.success(SceneHelper.i().setSceneInfo(page));
    }

    @ApiOperation("碳配额统计")
    @PostMapping("statics")
    @RequiresPermissions("ecp-admin:carbonQuota:search")
    public ResultResp<CarbonQuotaStaticsResp> statics(@RequestBody CarbonQuotaQueryReq req) {
        LocalDateTime startTime = StringUtils.hasLength(req.getStartTime()) ? LocalDate.parse(req.getStartTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay() : null;
        LocalDateTime endTime = StringUtils.hasLength(req.getEndTime()) ? LocalDate.parse(req.getEndTime(), DateTimeFormatter.ofPattern("yyyy-MM-dd")).atTime(LocalTime.MAX) : LocalDateTime.now();
        CarbonQuotaStaticsResp staticsResp = new CarbonQuotaStaticsResp();
        List<CarbonQuotaEntity> quotaEntities = carbonQuotaService.list(new LambdaQueryWrapper<CarbonQuotaEntity>()
                .eq(StringUtils.hasLength(req.getSceneProjectId()), CarbonQuotaEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.hasLength(req.getSceneEdificeId()), CarbonQuotaEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.hasLength(req.getSceneFloorId()), CarbonQuotaEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.hasLength(req.getSceneRoomId()), CarbonQuotaEntity::getSceneRoomId, req.getSceneRoomId())
                .ge(Objects.nonNull(startTime), CarbonQuotaEntity::getCreateTm, startTime)
                .le(CarbonQuotaEntity::getCreateTm, endTime)
        );
        Map<String, List<CarbonQuotaEntity>> inputTypeMap = quotaEntities.stream().collect(Collectors.groupingBy(CarbonQuotaEntity::getInputType));
        BigDecimal totalValue = inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.ADD.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        staticsResp.setTotalValue(totalValue);
        staticsResp.setSaleValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.SOLD.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add));
        staticsResp.setPurchasedValue(inputTypeMap.getOrDefault(CarbonQuotaInputTypeEnums.PURCHASE.getValue(), Collections.emptyList())
                .stream().map(CarbonQuotaEntity::getQuotaValue).reduce(BigDecimal.ZERO, BigDecimal::add));

        staticsResp.setUseValue(getUsedValue(req.getSceneProjectId(), req.getSceneEdificeId(),
                req.getSceneFloorId(), req.getSceneRoomId(), req.getStartTime(), req.getEndTime()));

        staticsResp.setRemainingValue(staticsResp.getTotalValue()
                .subtract(staticsResp.getUseValue())
                .add(staticsResp.getPurchasedValue())
                .subtract(staticsResp.getSaleValue()).setScale(2, RoundingMode.HALF_UP));

        BigDecimal income = BigDecimal.ZERO;
        for (CarbonQuotaEntity item : quotaEntities) {
            if (Objects.isNull(item.getCarbonIncome())) {
                income = income.add(carbonQuotaService.calculateCarbonIncome(item.getSceneProjectId(), item.getInputType(), item.getQuotaValue(), item.getCreateTm()));
            } else {
                income = income.add(item.getCarbonIncome());
            }
        }
        staticsResp.setCarbonIncome(income);
        return ResultResp.success(staticsResp);
    }

    private BigDecimal getUsedValue(String sceneProjectId, String sceneEdificeId, String sceneFloorId,
                                    String sceneRoomId, String start, String end) {
        BigDecimal res = Joins.of(EnergyDataEntity.class)
                .selectAs(builder -> builder
                        .add("SUM(carbon_emission)", "carbonEmission", false)
                )
                .leftJoin(MeterDeviceEntity.class, MeterDeviceEntity::getId, EnergyDataEntity::getMeterDeviceId)
                .notIn(MeterDeviceEntity::getReadMeterType, IMeterDeviceEnums.ReadMeterType.getZeroCarbonList())
                .end()
                .eq(StringUtils.hasLength(sceneProjectId), EnergyDataEntity::getSceneProjectId, sceneProjectId)
                .eq(StringUtils.hasLength(sceneEdificeId), EnergyDataEntity::getEnergyTypeId, sceneEdificeId)
                .eq(StringUtils.hasLength(sceneFloorId), EnergyDataEntity::getSceneEdificeId, sceneFloorId)
                .eq(StringUtils.hasLength(sceneRoomId), EnergyDataEntity::getSceneFloorId, sceneRoomId)
                .ge(StringUtils.hasLength(start), EnergyDataEntity::getSolidifyDate, start)
                .le(StringUtils.hasLength(end), EnergyDataEntity::getSolidifyDate, end)
                .joinGetOne(BigDecimal.class);
        return Objects.isNull(res) ? BigDecimal.ZERO : res.setScale(2, RoundingMode.HALF_UP);
    }

    @ApiOperation("新增")
    @PostMapping("add")
    @RequiresPermissions("ecp-admin:carbonQuota:add")
    public ResultResp<Boolean> add(@RequestBody @Validated({IAdd.class}) CarbonQuotaEntity req) {
        if (Objects.isNull(req.getCreateTm())) {
            req.setCreateTm(new Date());
        }
        req.setCarbonIncome(carbonQuotaService.calculateCarbonIncome(req.getSceneProjectId(), req.getInputType(), req.getQuotaValue(), req.getCreateTm()));
        boolean res = carbonQuotaService.save(req);
        return ResultResp.success(res);
    }

    @ApiOperation("编辑")
    @PostMapping("update")
    @RequiresPermissions("ecp-admin:carbonQuota:update")
    public ResultResp<Boolean> update(@RequestBody @Validated({IUpdate.class}) CarbonQuotaEntity req) {
        CarbonQuotaEntity carbonQuotaEntity = carbonQuotaService.getById(req.getId());
        if (Objects.isNull(carbonQuotaEntity)) {
            return ResultResp.fail("更新数据不存在");
        }
        req.setCarbonIncome(carbonQuotaService.calculateCarbonIncome(req.getSceneProjectId(), req.getInputType(), req.getQuotaValue(), carbonQuotaEntity.getCreateTm()));
        boolean res = carbonQuotaService.updateById(req);
        return ResultResp.success(res);
    }

    @ApiOperation("删除(根据Id删)")
    @PostMapping("deleteById")
    @RequiresPermissions("ecp-admin:carbonQuota:deleteById")
    public ResultResp<Boolean> deleteById(@RequestBody @Validated({IRequired.class}) BaseIdReq req) {
        return ResultResp.success(carbonQuotaService.removeById(req.getId()));
    }

    @ApiOperation("批量删除")
    @PostMapping("batchDelete")
    @RequiresPermissions("ecp-admin:carbonQuota:batchDelete")
    public ResultResp<Boolean> batchDelete(@RequestBody @Validated({IRequired.class}) BaseBatchIdReq req) {
        return ResultResp.success(carbonQuotaService.removeBatchByIds(req.getIds(), 1000));
    }

    private static JoinLambdaWrapper<CarbonQuotaEntity> buildQW(CarbonQuotaQueryReq req) {
        return Joins.of(CarbonQuotaEntity.class)
                .leftJoin(SceneProjectEntity.class, SceneProjectEntity::getId, CarbonQuotaEntity::getSceneProjectId)
                .selectAs(SceneProjectEntity::getProjectName, CarbonQuotaResp::getProjectName)
                .end()
                .eq(StringUtils.hasLength(req.getSceneProjectId()), CarbonQuotaEntity::getSceneProjectId, req.getSceneProjectId())
                .eq(StringUtils.hasLength(req.getSceneEdificeId()), CarbonQuotaEntity::getSceneEdificeId, req.getSceneEdificeId())
                .eq(StringUtils.hasLength(req.getSceneFloorId()), CarbonQuotaEntity::getSceneFloorId, req.getSceneFloorId())
                .eq(StringUtils.hasLength(req.getSceneRoomId()), CarbonQuotaEntity::getSceneRoomId, req.getSceneRoomId())
                .ge(StringUtils.hasLength(req.getStartTime()), CarbonQuotaEntity::getCreateTm, req.getStartTime())
                .le(StringUtils.hasLength(req.getEndTime()), CarbonQuotaEntity::getCreateTm, req.getEndTime())
                .orderByDesc(CarbonQuotaEntity::getCreateTm);
    }

}
